use crate::metrics::{QualityMetrics};
use crate::{Serialize, Deserialize};
use codegraph_core::{SyntaxNode, Result as CoreResult};
use std::collections::{HashMap, HashSet};
use tracing::{debug};

/// Quality analyzer for code
pub struct QualityAnalyzer {
    /// Configuration for quality analysis
    config: QualityConfig,
    /// Cache for quality calculations
    cache: HashMap<String, QualityMetrics>,
}

/// Configuration for quality analysis
#[derive(Debug, Clone)]
pub struct QualityConfig {
    /// Enable duplication detection
    pub enable_duplication: bool,
    /// Enable code smell detection
    pub enable_code_smells: bool,
    /// Enable security analysis
    pub enable_security: bool,
    /// Enable performance analysis
    pub enable_performance: bool,
    /// Minimum clone size for duplication detection
    pub min_clone_size: usize,
    /// Maximum line length
    pub max_line_length: usize,
    /// Maximum function length
    pub max_function_length: usize,
    /// Maximum parameter count
    pub max_parameter_count: usize,
}

impl Default for QualityConfig {
    fn default() -> Self {
        Self {
            enable_duplication: true,
            enable_code_smells: true,
            enable_security: true,
            enable_performance: true,
            min_clone_size: 50,
            max_line_length: 120,
            max_function_length: 50,
            max_parameter_count: 5,
        }
    }
}

/// Code smell types
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CodeSmell {
    LongMethod,
    LongParameterList,
    LargeClass,
    DuplicatedCode,
    LongLineOfCode,
    DeepNesting,
    ComplexConditional,
    MagicNumber,
    DeadCode,
    UnusedVariable,
    UnusedImport,
    InconsistentNaming,
    MissingDocumentation,
    TooManyComments,
    EmptyBlock,
    UnnecessaryComplexity,
}

/// Security issue types
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SecurityIssue {
    SqlInjection,
    XssVulnerability,
    HardcodedCredentials,
    WeakCryptography,
    InsecureRandomness,
    PathTraversal,
    CommandInjection,
    BufferOverflow,
    IntegerOverflow,
    UnvalidatedInput,
    InsecureDeserialization,
    WeakAuthentication,
}

/// Performance issue types
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PerformanceIssue {
    InefficiientLoop,
    UnnecessaryObjectCreation,
    StringConcatenationInLoop,
    UnoptimizedQuery,
    MemoryLeak,
    ExcessiveMemoryUsage,
    SlowAlgorithm,
    BlockingOperation,
    UnboundedResource,
    CacheMiss,
}

/// Quality issue
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityIssue {
    /// Type of issue
    pub issue_type: QualityIssueType,
    /// Severity level
    pub severity: IssueSeverity,
    /// Description of the issue
    pub description: String,
    /// Location in the code
    pub location: IssueLocation,
    /// Suggested fix
    pub suggestion: Option<String>,
    /// Confidence level (0.0 to 1.0)
    pub confidence: f64,
}

/// Quality issue types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QualityIssueType {
    CodeSmell(CodeSmell),
    Security(SecurityIssue),
    Performance(PerformanceIssue),
}

/// Issue severity levels
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum IssueSeverity {
    Info,
    Minor,
    Major,
    Critical,
    Blocker,
}

/// Issue location
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssueLocation {
    /// File path
    pub file: String,
    /// Start line
    pub start_line: usize,
    /// End line
    pub end_line: usize,
    /// Start column
    pub start_column: usize,
    /// End column
    pub end_column: usize,
}

impl QualityAnalyzer {
    /// Create a new quality analyzer
    pub fn new(config: QualityConfig) -> Self {
        Self {
            config,
            cache: HashMap::new(),
        }
    }

    /// Analyze quality of a syntax tree
    pub async fn analyze_quality(
        &mut self,
        root: &dyn SyntaxNode,
        file_path: &str,
        content: &str,
    ) -> CoreResult<(QualityMetrics, Vec<QualityIssue>)> {
        debug!("Analyzing quality for file: {}", file_path);

        let mut metrics = QualityMetrics::default();
        let mut issues = Vec::new();

        // Detect code duplication
        if self.config.enable_duplication {
            let duplication = self.detect_duplication(content).await?;
            metrics.duplication_percentage = duplication;
        }

        // Detect code smells
        if self.config.enable_code_smells {
            let (smell_count, smell_issues) = self.detect_code_smells(root, file_path, content).await?;
            metrics.code_smells = smell_count;
            issues.extend(smell_issues);
        }

        // Detect security issues
        if self.config.enable_security {
            let (security_count, security_issues) = self.detect_security_issues(root, file_path, content).await?;
            metrics.security_issues = security_count;
            issues.extend(security_issues);
        }

        // Detect performance issues
        if self.config.enable_performance {
            let (perf_count, perf_issues) = self.detect_performance_issues(root, file_path, content).await?;
            metrics.performance_issues = perf_count;
            issues.extend(perf_issues);
        }

        // Calculate test coverage (placeholder)
        metrics.test_coverage = self.estimate_test_coverage(content).await?;

        // Calculate technical debt ratio
        metrics.technical_debt_ratio = self.calculate_technical_debt_ratio(&issues).await?;

        // Calculate documentation coverage
        metrics.documentation_coverage = self.calculate_documentation_coverage(root, content).await?;

        debug!("Quality analysis complete: {} issues found", issues.len());

        Ok((metrics, issues))
    }

    /// Detect code duplication
    async fn detect_duplication(&self, content: &str) -> CoreResult<f64> {
        let lines: Vec<&str> = content.lines().collect();
        let total_lines = lines.len();
        
        if total_lines == 0 {
            return Ok(0.0);
        }

        let mut duplicated_lines = HashSet::new();
        let min_size = self.config.min_clone_size.min(total_lines);

        // Simple line-based duplication detection
        for i in 0..total_lines.saturating_sub(min_size) {
            for j in (i + min_size)..total_lines.saturating_sub(min_size) {
                let mut match_length = 0;
                
                while i + match_length < total_lines &&
                      j + match_length < total_lines &&
                      lines[i + match_length].trim() == lines[j + match_length].trim() &&
                      !lines[i + match_length].trim().is_empty() {
                    match_length += 1;
                }

                if match_length >= min_size {
                    for k in 0..match_length {
                        duplicated_lines.insert(i + k);
                        duplicated_lines.insert(j + k);
                    }
                }
            }
        }

        let duplication_percentage = (duplicated_lines.len() as f64 / total_lines as f64) * 100.0;
        Ok(duplication_percentage)
    }

    /// Detect code smells
    async fn detect_code_smells(
        &self,
        root: &dyn SyntaxNode,
        file_path: &str,
        content: &str,
    ) -> CoreResult<(usize, Vec<QualityIssue>)> {
        let mut issues = Vec::new();

        // Check for long lines
        self.check_long_lines(content, file_path, &mut issues).await?;

        // Check for long methods
        self.check_long_methods(root, file_path, &mut issues).await?;

        // Check for too many parameters
        self.check_parameter_count(root, file_path, &mut issues).await?;

        // Check for deep nesting
        self.check_deep_nesting(root, file_path, &mut issues).await?;

        // Check for magic numbers
        self.check_magic_numbers(root, file_path, &mut issues).await?;

        // Check for empty blocks
        self.check_empty_blocks(root, file_path, &mut issues).await?;

        // Check for missing documentation
        self.check_missing_documentation(root, file_path, &mut issues).await?;

        Ok((issues.len(), issues))
    }

    /// Check for long lines
    async fn check_long_lines(
        &self,
        content: &str,
        file_path: &str,
        issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        for (line_num, line) in content.lines().enumerate() {
            if line.len() > self.config.max_line_length {
                issues.push(QualityIssue {
                    issue_type: QualityIssueType::CodeSmell(CodeSmell::LongLineOfCode),
                    severity: IssueSeverity::Minor,
                    description: format!("Line too long: {} characters (max: {})", 
                                       line.len(), self.config.max_line_length),
                    location: IssueLocation {
                        file: file_path.to_string(),
                        start_line: line_num + 1,
                        end_line: line_num + 1,
                        start_column: self.config.max_line_length,
                        end_column: line.len(),
                    },
                    suggestion: Some("Consider breaking this line into multiple lines".to_string()),
                    confidence: 1.0,
                });
            }
        }
        Ok(())
    }

    /// Check for long methods
    async fn check_long_methods(
        &self,
        root: &dyn SyntaxNode,
        file_path: &str,
        issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        self.traverse_for_long_methods(root, file_path, issues).await
    }

    /// Traverse tree for long methods
    async fn traverse_for_long_methods(
        &self,
        node: &dyn SyntaxNode,
        file_path: &str,
        issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        let node_kind = node.kind();

        if matches!(node_kind, "function_definition" | "method_definition") {
            let function_lines = self.count_function_lines(node);
            if function_lines > self.config.max_function_length {
                let function_name = self.extract_function_name(node).unwrap_or_else(|| "anonymous".to_string());
                
                issues.push(QualityIssue {
                    issue_type: QualityIssueType::CodeSmell(CodeSmell::LongMethod),
                    severity: IssueSeverity::Major,
                    description: format!("Function '{}' is too long: {} lines (max: {})", 
                                       function_name, function_lines, self.config.max_function_length),
                    location: self.node_to_location(node, file_path),
                    suggestion: Some("Consider breaking this function into smaller functions".to_string()),
                    confidence: 0.9,
                });
            }
        }

        // Process children
        for child in node.children() {
            Box::pin(self.traverse_for_long_methods(child.as_ref(), file_path, issues)).await?;
        }

        Ok(())
    }

    /// Count lines in a function
    fn count_function_lines(&self, node: &dyn SyntaxNode) -> usize {
        let start_pos = node.start_position();
        let end_pos = node.end_position();
        ((end_pos.row - start_pos.row) + 1) as usize
    }

    /// Extract function name from a function node
    fn extract_function_name(&self, node: &dyn SyntaxNode) -> Option<String> {
        for child in node.children() {
            if child.kind() == "identifier" {
                return Some(child.text().to_string());
            }
        }
        None
    }

    /// Convert node to issue location
    fn node_to_location(&self, node: &dyn SyntaxNode, file_path: &str) -> IssueLocation {
        let start_pos = node.start_position();
        let end_pos = node.end_position();
        
        IssueLocation {
            file: file_path.to_string(),
            start_line: (start_pos.row + 1) as usize,
            end_line: (end_pos.row + 1) as usize,
            start_column: (start_pos.column + 1) as usize,
            end_column: (end_pos.column + 1) as usize,
        }
    }

    /// Check for too many parameters
    async fn check_parameter_count(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would traverse and count parameters
        // Placeholder for now
        Ok(())
    }

    /// Check for deep nesting
    async fn check_deep_nesting(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would check nesting depth
        // Placeholder for now
        Ok(())
    }

    /// Check for magic numbers
    async fn check_magic_numbers(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would detect magic numbers
        // Placeholder for now
        Ok(())
    }

    /// Check for empty blocks
    async fn check_empty_blocks(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would detect empty blocks
        // Placeholder for now
        Ok(())
    }

    /// Check for missing documentation
    async fn check_missing_documentation(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would check for missing docstrings/comments
        // Placeholder for now
        Ok(())
    }

    /// Detect security issues
    async fn detect_security_issues(
        &self,
        root: &dyn SyntaxNode,
        file_path: &str,
        content: &str,
    ) -> CoreResult<(usize, Vec<QualityIssue>)> {
        let mut issues = Vec::new();

        // Check for hardcoded credentials
        self.check_hardcoded_credentials(content, file_path, &mut issues).await?;

        // Check for SQL injection patterns
        self.check_sql_injection(root, file_path, &mut issues).await?;

        // Check for XSS vulnerabilities
        self.check_xss_vulnerabilities(root, file_path, &mut issues).await?;

        Ok((issues.len(), issues))
    }

    /// Check for hardcoded credentials
    async fn check_hardcoded_credentials(
        &self,
        content: &str,
        file_path: &str,
        issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        let patterns = [
            "password", "passwd", "pwd", "secret", "key", "token", "api_key",
            "access_key", "private_key", "auth", "credential"
        ];

        for (line_num, line) in content.lines().enumerate() {
            let line_lower = line.to_lowercase();
            for pattern in &patterns {
                if line_lower.contains(pattern) && line.contains('=') && 
                   (line.contains('"') || line.contains('\'')) {
                    issues.push(QualityIssue {
                        issue_type: QualityIssueType::Security(SecurityIssue::HardcodedCredentials),
                        severity: IssueSeverity::Critical,
                        description: "Potential hardcoded credential detected".to_string(),
                        location: IssueLocation {
                            file: file_path.to_string(),
                            start_line: line_num + 1,
                            end_line: line_num + 1,
                            start_column: 1,
                            end_column: line.len(),
                        },
                        suggestion: Some("Use environment variables or secure configuration".to_string()),
                        confidence: 0.7,
                    });
                    break;
                }
            }
        }
        Ok(())
    }

    /// Check for SQL injection patterns
    async fn check_sql_injection(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would detect SQL injection patterns
        // Placeholder for now
        Ok(())
    }

    /// Check for XSS vulnerabilities
    async fn check_xss_vulnerabilities(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _issues: &mut Vec<QualityIssue>,
    ) -> CoreResult<()> {
        // Implementation would detect XSS patterns
        // Placeholder for now
        Ok(())
    }

    /// Detect performance issues
    async fn detect_performance_issues(
        &self,
        _root: &dyn SyntaxNode,
        _file_path: &str,
        _content: &str,
    ) -> CoreResult<(usize, Vec<QualityIssue>)> {
        // Implementation would detect performance anti-patterns
        // Placeholder for now
        Ok((0, Vec::new()))
    }

    /// Estimate test coverage
    async fn estimate_test_coverage(&self, content: &str) -> CoreResult<f64> {
        // Simple heuristic: look for test-related keywords
        let total_lines = content.lines().count();
        if total_lines == 0 {
            return Ok(0.0);
        }

        let test_lines = content.lines()
            .filter(|line| {
                let line_lower = line.to_lowercase();
                line_lower.contains("test") || line_lower.contains("assert") ||
                line_lower.contains("expect") || line_lower.contains("should")
            })
            .count();

        let coverage = (test_lines as f64 / total_lines as f64) * 100.0;
        Ok(coverage.min(100.0))
    }

    /// Calculate technical debt ratio
    async fn calculate_technical_debt_ratio(&self, issues: &[QualityIssue]) -> CoreResult<f64> {
        if issues.is_empty() {
            return Ok(0.0);
        }

        let total_severity: f64 = issues.iter()
            .map(|issue| match issue.severity {
                IssueSeverity::Info => 1.0,
                IssueSeverity::Minor => 2.0,
                IssueSeverity::Major => 4.0,
                IssueSeverity::Critical => 8.0,
                IssueSeverity::Blocker => 16.0,
            })
            .sum();

        // Normalize to 0-1 range
        let ratio = (total_severity / (issues.len() as f64 * 16.0)).min(1.0);
        Ok(ratio)
    }

    /// Calculate documentation coverage
    async fn calculate_documentation_coverage(&self, root: &dyn SyntaxNode, content: &str) -> CoreResult<f64> {
        let total_functions = self.count_functions(root).await?;
        if total_functions == 0 {
            return Ok(100.0);
        }

        let documented_functions = self.count_documented_functions(root, content).await?;
        let coverage = (documented_functions as f64 / total_functions as f64) * 100.0;
        Ok(coverage)
    }

    /// Count total functions
    async fn count_functions(&self, root: &dyn SyntaxNode) -> CoreResult<usize> {
        let mut count = 0;
        self.traverse_count_functions(root, &mut count).await?;
        Ok(count)
    }

    /// Traverse and count functions
    async fn traverse_count_functions(&self, node: &dyn SyntaxNode, count: &mut usize) -> CoreResult<()> {
        if matches!(node.kind(), "function_definition" | "method_definition") {
            *count += 1;
        }

        for child in node.children() {
            Box::pin(self.traverse_count_functions(child.as_ref(), count)).await?;
        }

        Ok(())
    }

    /// Count documented functions
    async fn count_documented_functions(&self, root: &dyn SyntaxNode, content: &str) -> CoreResult<usize> {
        // Simple heuristic: functions with preceding comments or docstrings
        let lines: Vec<&str> = content.lines().collect();
        let mut documented_count = 0;

        self.traverse_check_documentation(root, &lines, &mut documented_count).await?;
        Ok(documented_count)
    }

    /// Traverse and check for documentation
    async fn traverse_check_documentation(
        &self,
        node: &dyn SyntaxNode,
        lines: &[&str],
        documented_count: &mut usize,
    ) -> CoreResult<()> {
        if matches!(node.kind(), "function_definition" | "method_definition") {
            let start_line = node.start_position().row as usize;

            // Check if there's a comment or docstring before the function
            if start_line > 0 {
                let prev_line = lines.get(start_line - 1).unwrap_or(&"").trim();
                if prev_line.starts_with('#') || prev_line.starts_with("//") ||
                   prev_line.starts_with("/*") || prev_line.starts_with("\"\"\"") ||
                   prev_line.starts_with("'''") {
                    *documented_count += 1;
                }
            }
        }

        for child in node.children() {
            Box::pin(self.traverse_check_documentation(child.as_ref(), lines, documented_count)).await?;
        }

        Ok(())
    }
}
